-- HEADER
local Capping = CreateFrame("Button", "Capping", UIParent)
local self = Capping
local L = CappingLocale

-- LIBRARIES
local media = LibStub("LibSharedMedia-3.0")
media:Register("statusbar", "BantoBarReverse", "Interface\\AddOns\\Capping\\textures\\BantoBarReverse")

-- GLOBALS MADE LOCAL
local _G = getfenv(0)
local format, strfind, strlower, select, type = format, strfind, strlower, select, type
local min, max, floor, math_sin, math_pi, tonumber = min, max, floor, math.sin, math.pi, tonumber
local GetTime, time = GetTime, time
local GetWorldStateUIInfo, GetWintergraspWaitTime = GetWorldStateUIInfo, GetWintergraspWaitTime

-- LOCAL VARS
local db, f, wasInBG, bgmap, realm, pname
local activebars, bars, currentq, bgdd = { }, { }, { }, { }
local av, ab, eots, wsg, winter = L["Alterac Valley"], L["Arathi Basin"], L["Eye of the Storm"], L["Warsong Gulch"], L["Wintergrasp"]
local backdrop = { bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", }
local stamin = 1/60
local narrowed, borderhidden, pointset, ACountText, HCountText

-- LOCAL FUNCS
local ShowOptions
local oSetPoint = Capping.SetPoint
local function IsInBG()
	return select(2, IsInInstance()) == "pvp"
end
local function IsInArena()
	return select(2, IsInInstance()) == "arena"
end
local function ToggleAnchor()
	if f:IsShown() then
		f:Hide()
	else 
		f:Show() 
	end
end
local function SetPoints(this, lp, lrt, lrp, lx, ly, rp, rrt, rrp, rx, ry)
	this:ClearAllPoints()
	this:SetPoint(lp, lrt, lrp, lx, ly)
	if rp then this:SetPoint(rp, rrt, rrp, rx, ry) end
end
local function SetWH(this, w, h)
	this:SetWidth(w)
	this:SetHeight(h)
end
local function NewText(parent, font, fontsize, justifyH, justifyV, overlay)
	local t = parent:CreateFontString(nil, overlay or "OVERLAY")
	if fontsize then
		t:SetFont(font, fontsize)
		t:SetShadowColor(0, 0, 0)
		t:SetShadowOffset(1, -1)
	else
		t:SetFontObject(font)
	end
	t:SetJustifyH(justifyH) 
	t:SetJustifyV(justifyV)
	return t
end
local function StartMoving(this) this:StartMoving() end
local function CreateMover(oldframe, w, h, dragstopfunc)
	local mover = oldframe or CreateFrame("Button", nil, UIParent)
	SetWH(mover, w, h)
	mover:SetBackdrop(backdrop)
	mover:SetBackdropColor(0, 0, 0, 0.7)
	mover:SetMovable(true)
	mover:RegisterForDrag("LeftButton")
	mover:SetScript("OnDragStart", StartMoving)
	mover:SetScript("OnDragStop", dragstopfunc)
	mover:SetClampedToScreen(true)
	mover:SetFrameStrata("HIGH")
	mover.close = CreateFrame("Button", nil, mover, "UIPanelCloseButton")
	SetWH(mover.close, 20, 20)
	mover.close:SetPoint("TOPRIGHT", 5, 5)
	return mover
end 
hooksecurefunc(WorldStateAlwaysUpFrame, "SetPoint", function()
	if not db or not db.sbx then return end
	oSetPoint(WorldStateAlwaysUpFrame, "TOP", UIParent, "TOPLEFT", db.sbx, db.sby)
end)
local function wsaufu()
	if not db or not db.cbx then return end
	local nexty = 0
	for i=1, NUM_EXTENDED_UI_FRAMES do
		local cb = _G["WorldStateCaptureBar"..i]
		if cb and cb:IsShown() then
			cb:SetPoint("TOPRIGHT", UIParent, "BOTTOMLEFT", db.cbx, db.cby - nexty)
			nexty = nexty + cb:GetHeight()
		end
	end
end
hooksecurefunc("WorldStateAlwaysUpFrame_Update", wsaufu)
hooksecurefunc(VehicleSeatIndicator, "SetPoint", function()
	if not db or not db.seatx then return end
	oSetPoint(VehicleSeatIndicator, "TOPRIGHT", UIParent, "BOTTOMLEFT", db.seatx, db.seaty)
end)
local function UpdateZoneMapVisibility()
	if (not bgmap or not bgmap:IsShown()) and GetCVar("showBattlefieldMinimap") ~= "0" then
		if not bgmap then
			LoadAddOn("Blizzard_BattlefieldMinimap")
		end
		bgmap:Show()
	end
end
hooksecurefunc("WorldMapZoneMinimapDropDown_OnClick", function()
	if GetMapInfo() == "LakeWintergrasp" then
		UpdateZoneMapVisibility()
	end
end)


-- EVENT HANDLERS
local elist, clist = {}, {}
Capping:SetScript("OnEvent", function(this, event, ...)
	this[elist[event] or event](this, ...)
end)
function Capping:RegisterTempEvent(event, other)
	self:RegisterEvent(event)
	elist[event] = other or event
end
function Capping:CheckCombat(func)
	if InCombatLockdown() then
		self:RegisterEvent("PLAYER_REGEN_ENABLED")
		tinsert(clist, func)
	else
		func(self)
	end
end
function Capping:PLAYER_REGEN_ENABLED()
	for k,v in ipairs(clist) do
		v(self)
		clist[k] = nil
	end
	self:UnregisterEvent("PLAYER_REGEN_ENABLED")
end


Capping:RegisterEvent("ADDON_LOADED")
---------------------------------
function Capping:ADDON_LOADED(a1)
---------------------------------
	if a1 ~= "Capping" then return end
	
	-- saved variables database setup
	CappingDB = CappingDB or {}
	db = (CappingDB.profiles and CappingDB.profiles.Default) or CappingDB
	self.db = db
	if db.dbinit ~= 5 then 
		db.dbinit = 5
		local function SetDefaults(db, t)
			for k,v in pairs(t) do
				if type(db[k]) == "table" then
					SetDefaults(db[k], v)
				else
					db[k] = (db[k] ~= nil and db[k]) or v
				end
			end
		end
		SetDefaults(db, {
			av = true, avquest = true, ab = true, wsg = true, arena = true, eots = true, winter = true,
			port = true, wait = true,
			mapscale = 1.3, narrow = true, hidemapborder = false,
			texture = "BantoBarReverse",
			width = 200, height = 15,
			inset = 1, spacing = 1,
			mainup = false, reverse = false, fill = false,
			iconpos = L["LEFT"], timepos = L["LEFT"],
			font = "Friz Quadrata TT", fontsize = 11,
			colors = {
				alliance = { r=0.0, g=0.0, b=1.0, a=1.0, },
				horde = { r=1.0, g=0.0, b=0.0, a=1.0, },
				info1 = { r=0.6, g=0.6, b=0.6, a=1.0, },
				info2 = { r=1.0, g=1.0, b=0.0, a=1.0, },
				font = { r=1, g=1, b=1, a=1, },
			},
		})
	end
  	SlashCmdList.CAPPING = ShowOptions
   	SLASH_CAPPING1 = "/capping"
   	
   	realm = GetCVar("realmName")
	pname = UnitName("player")

   	-- adds Capping config to default UI Interface Options
	local panel = CreateFrame("Frame")
	panel.name = "Capping"
	panel:SetScript("OnShow", function(this)
		local t1 = NewText(this, GameFontNormalLarge, nil, "LEFT", "TOP", "ARTWORK")
		t1:SetPoint("TOPLEFT", 16, -16)
		t1:SetText(this.name)
		
		local t2 = NewText(this, GameFontHighlightSmall, nil, "LEFT", "TOP", "ARTWORK")
		t2:SetHeight(43)
		SetPoints(t2, "TOPLEFT", t1, "BOTTOMLEFT", 0, -8, "RIGHT", this, "RIGHT", -32, 0)
		t2:SetNonSpaceWrap(true)
		local function GetInfo(field)
			return GetAddOnMetadata("Capping", field) or "N/A"
		end
		t2:SetFormattedText("Notes: %s\nAuthor: %s\nVersion: %s", GetInfo("Notes"), GetInfo("Author"), GetInfo("Version"))
	
		local b = CreateFrame("Button", nil, this, "UIPanelButtonTemplate")
		SetWH(b, 120, 20)
		b:SetText("Options Menu")
		b:SetScript("OnClick", ShowOptions)
		b:SetPoint("TOPLEFT", t2, "BOTTOMLEFT", -2, -8)
		this:SetScript("OnShow", nil)
	end)
	InterfaceOptions_AddCategory(panel)
	
	-- anchor frame
	f = self
	f:Hide()
	if db.x then
		f:SetPoint(db.p or "TOPLEFT", UIParent, db.rp or "TOPLEFT", db.x, db.y)	
	else
		f:SetPoint("CENTER", UIParent, "CENTER", 200, -100)
	end
	CreateMover(f, db.width, 10, function(this) 
		this:StopMovingOrSizing()
		local a,b,c,d,e = this:GetPoint()
		db.p, db.rp, db.x, db.y = a, c, floor(d + 0.5), floor(e + 0.5)
	end)
	f:RegisterForClicks("RightButtonUp")
	f:SetScript("OnClick", ShowOptions)
	f:SetNormalFontObject(GameFontHighlightSmall)
	f:SetText("Capping")

	if db.sbx then WorldStateAlwaysUpFrame:SetPoint("TOP") end  -- world state info frame positioning
	if db.cbx then wsaufu() end  -- capturebar position
	if db.seatx then VehicleSeatIndicator:SetPoint("TOPRIGHT") end  -- vehicle seat position
	
	if BattlefieldMinimap then  -- battlefield minimap setup
		self:InitBGMap()
	else
		function Capping:ADDON_LOADED(a1)
			if a1 ~= "Blizzard_BattlefieldMinimap" then return end
			self:InitBGMap()
		end
	end
	if IsLoggedIn() then
		self:PLAYER_ENTERING_WORLD()
	else
		self:RegisterEvent("PLAYER_ENTERING_WORLD")
	end
end

----------------------------
function Capping:InitBGMap()
----------------------------
	bgmap, bgtab = BattlefieldMinimap, BattlefieldMinimapTab
	bgmap:UnregisterEvent("PLAYER_LOGOUT")
	bgmap:UnregisterEvent("ADDON_LOADED")
	
	bgdd.text, bgdd.func = "Capping", ShowOptions
	hooksecurefunc("BattlefieldMinimapTabDropDown_Initialize", function() UIDropDownMenu_AddButton(bgdd, 1) end)
	BattlefieldMinimap_OnEvent(nil, "ADDON_LOADED", "Blizzard_BattlefieldMinimap")
	local bgopts = BattlefieldMinimapOptions
	
	hooksecurefunc(bgtab, "StopMovingOrSizing", function()
		bgopts.position = bgopts.position or { }
		local x, y = bgtab:GetCenter()
		bgopts.position.x, bgopts.position.y = floor(x - (narrowed and pointset and 153 or 0) + 0.5), floor(y + 0.5)
	end)
	if bgopts.position then
		bgtab:SetPoint("CENTER", UIParent, "BOTTOMLEFT", bgopts.position.x, bgopts.position.y)
	end
	self:ModMap()
	self:UnregisterEvent("ADDON_LOADED")
	self.InitBGMap, self.ADDON_LOADED = nil, nil
end


local lastsync = 0
local StartWinterGraspTimer = function(duration)
	if not duration then return end
	duration = floor(duration * 1.025)
	db[realm] = time() + duration
	Capping:StartBar(winter, (duration > 950 and 9225) or 923, duration, "Interface\\Icons\\INV_EssenceOfWintergrasp", "info1", 1500, true)
end

----------------------------------------
function Capping:PLAYER_ENTERING_WORLD()
----------------------------------------
	self:UnregisterEvent("PLAYER_ENTERING_WORLD")
	self:RegisterEvent("ZONE_CHANGED_NEW_AREA")
	self:RegisterEvent("UPDATE_BATTLEFIELD_STATUS")
	self:UPDATE_BATTLEFIELD_STATUS()
	self:ZoneCheck()
	if db.winter then
		if GetWintergraspWaitTime() then
			StartWinterGraspTimer(GetWintergraspWaitTime(), true)
		else
			if db[realm] and time() < db[realm] then
				self:StartBar(winter, 9225, db[realm] - time(), "Interface\\Icons\\INV_EssenceOfWintergrasp", "info1", 1500, true)
			end
		end
	else
		db[realm] = nil
	end
	self.PLAYER_ENTERING_WORLD = nil
end

----------------------------------------
function Capping:ZONE_CHANGED_NEW_AREA()
----------------------------------------
	if wasInBG then
		self:ResetAll()
	end
	self:ZoneCheck()
end


local tohide = { }
local function HideProtectedStuff()
	for _, v in ipairs(tohide) do
		v:Hide()
	end
end
--------------------------------------
function Capping:AddFrameToHide(frame)  -- secure frames that are hidden upon zone change
--------------------------------------
	tinsert(tohide, frame)
end
---------------------------
function Capping:ResetAll()  -- reset all timers and unregister temp events
---------------------------
	wasInBG = false
	for event in pairs(elist) do  -- unregister all temp events
		elist[event] = nil
		self:UnregisterEvent(event)
	end
	for value, _ in pairs(activebars) do  -- close all temp timerbars
		self:StopBar(value)
		activebars[value] = nil
	end
	self:CheckCombat(HideProtectedStuff)  -- hide secure frames
	if ACountText then ACountText:SetText("") end
	if HCountText then HCountText:SetText("") end
end
local wscheck = 0
----------------------------
function Capping:ZoneCheck()  -- check if new zone is a battleground
----------------------------
	SetMapToCurrentZone()
	if IsInBG() then
		local z = GetRealZoneText()
		wasInBG = true
		if z == wsg and db.wsg then
			self:StartWSG()
		elseif z == eots and db.eots then
			self:StartEotS()
		elseif z == ab and db.ab then
			self:StartAB()
		elseif z == av and db.av then
			self:StartAV()
		end
		if not BGCheck then
			BGCheck = CreateFrame("Frame", nil, AlwaysUpFrame1)
			BGCheck:SetScript("OnUpdate", function(this, elapsed)
				this.elapsed = (this.elapsed or 0) + elapsed
				if this.elapsed < 4 then return end
				this.elapsed = 0
				RequestBattlefieldScoreData()
			end)
			self:AddFrameToHide(BGCheck)
		end
		BGCheck:Show()
		
		self:RegisterTempEvent("UPDATE_BATTLEFIELD_SCORE", "UpdateCountText")
		self:RegisterTempEvent("CHAT_MSG_BG_SYSTEM_NEUTRAL", "CheckStartTimer")
		RequestBattlefieldScoreData()
		
		UpdateZoneMapVisibility()
	elseif GetMapInfo() == "LakeWintergrasp" then
		if db.winter then
			Capping.WintergraspTimerZone = Capping.WintergraspTimerZone or function()
				local ctime = GetTime()
				if ctime < wscheck + 2 then return end
				wscheck = ctime
				local wgtime = GetWintergraspWaitTime()
				if not wgtime or wgtime < 1 then
					self:StopBar(winter)
				else
					StartWinterGraspTimer(wgtime)
				end
			end
			self:RegisterTempEvent("UPDATE_WORLD_STATES", "WintergraspTimerZone")
			wasInBG = true
		end
		UpdateZoneMapVisibility()
	else
		if IsInArena() then
			self:RegisterTempEvent("CHAT_MSG_BG_SYSTEM_NEUTRAL", "CheckStartTimer")
			wasInBG = true
		elseif db.winter and GetCurrentMapContinent() == 4 then
			StartWinterGraspTimer(GetWintergraspWaitTime())
		end
		if bgmap and bgmap:IsShown() and GetCVar("showBattlefieldMinimap") ~= "2" then
			bgmap:Hide()
		end
	end
	self:ModMap()
end

--------------------------------
function Capping:ModMap(disable)  -- alter the default minimap
--------------------------------
	if not bgmap then return end
	bgmap:SetScale(db.mapscale)
	disable = not IsInBG() or disable

	if db.narrow and not narrowed and not disable then  -- narrow setting
		BattlefieldMinimap1:Hide() BattlefieldMinimap4:Hide() BattlefieldMinimap5:Hide()
		BattlefieldMinimap8:Hide() BattlefieldMinimap9:Hide() BattlefieldMinimap12:Hide()
		BattlefieldMinimapBackground:SetWidth(256 / 2)
		BattlefieldMinimapBackground:SetPoint("TOPLEFT", -12 + 64, 12)
		BattlefieldMinimapCorner:SetPoint("TOPRIGHT", -2 - 52, 3 + 1)
		SetWH(BattlefieldMinimapCorner, 24, 24)
		BattlefieldMinimapCloseButton:SetPoint("TOPRIGHT", bgmap, "TOPRIGHT", 2 - 53, 7)
		SetWH(BattlefieldMinimapCloseButton, 24, 24)
		narrowed = 1
	elseif disable or (not db.narrow and narrowed) then  -- setting things back to blizz's default
		BattlefieldMinimap1:Show() BattlefieldMinimap4:Show() BattlefieldMinimap5:Show()
		BattlefieldMinimap8:Show() BattlefieldMinimap9:Show() BattlefieldMinimap12:Show()
		BattlefieldMinimapBackground:SetWidth(256)
		BattlefieldMinimapBackground:SetPoint("TOPLEFT", -12, 12)
		BattlefieldMinimapCorner:SetPoint("TOPRIGHT", -2, 3)
		SetWH(BattlefieldMinimapCorner, 32, 32)
		BattlefieldMinimapCloseButton:SetPoint("TOPRIGHT", bgmap, "TOPRIGHT", 2, 7)
		SetWH(BattlefieldMinimapCloseButton, 32, 32)
		narrowed = nil
	end

	if narrowed and not db.narrowanchorleft and not pointset then  -- set anchoring after map narrowing
		local tx, ty = bgtab:GetCenter()
		if tx then
			bgtab:ClearAllPoints()
			bgtab:SetPoint("CENTER", UIParent, "BOTTOMLEFT", tx + (116 * db.mapscale), ty)
			bgtab:SetUserPlaced(true)
			pointset = true
		end
	elseif not narrowed and pointset then
		local tx, ty = bgtab:GetCenter()
		bgtab:ClearAllPoints()
		bgtab:SetPoint("CENTER", UIParent, "BOTTOMLEFT", tx - (116 * db.mapscale), ty)
		pointset = nil
	end

	if db.hidemapborder and not borderhidden then  -- Hide border
		BattlefieldMinimapBackground:Hide()
		BattlefieldMinimapCorner:Hide()
		BattlefieldMinimapCloseButton:SetParent(bgtab)
		BattlefieldMinimapCloseButton:SetScale(db.mapscale)
		BattlefieldMinimapCloseButton:HookScript("OnClick", function() bgmap:Hide() end)
		borderhidden = true
	elseif not db.hidemapborder and borderhidden then  -- Show border
		BattlefieldMinimapBackground:Show()
		BattlefieldMinimapCorner:Show()
		BattlefieldMinimapCloseButton:SetParent(bgmap)
		BattlefieldMinimapCloseButton:SetScale(1)
		borderhidden = nil
	end
	bgmap:SetPoint("TOPLEFT", bgtab, "BOTTOMLEFT", (narrowed and -64) or 0, (borderhidden and 0) or -5)
end

do  -- estimated wait timer and the 2 min timer to port to a certain battleground
	local q, p = L["Queue: %s"], L["Port: %s"]
	local maxq = MAX_BATTLEFIELD_QUEUES
	local GetBattlefieldStatus = GetBattlefieldStatus
	local GetBattlefieldPortExpiration = GetBattlefieldPortExpiration
	local GetBattlefieldEstimatedWaitTime, GetBattlefieldTimeWaited = GetBattlefieldEstimatedWaitTime, GetBattlefieldTimeWaited
	--------------------------------------------
	function Capping:UPDATE_BATTLEFIELD_STATUS()
	--------------------------------------------
		if not db.port and not db.wait then return end
		
		for map in pairs(currentq) do  -- tag each entry to see if it's changed after updating the list
			currentq[map] = 0
		end
		for i = 1, maxq, 1 do  -- check the status of each queue
			local status, map, _, _, _, teamsize, _ = GetBattlefieldStatus(i)
			if map and teamsize and teamsize > 0 then -- arena
				map = format("%s (%dv%d)", map, teamsize, teamsize)
			end
			if status == "confirm" and db.port then
				self:StopBar(format(q, map))
				self:StartBar(format(p, map), 80, GetBattlefieldPortExpiration(i) * 0.001, "Interface\\Icons\\Ability_TownWatch", "info2", true, true)
				currentq[map] = i
			elseif status == "queued" and db.wait then
				local esttime = GetBattlefieldEstimatedWaitTime(i) * 0.001
				local estremain = esttime - GetBattlefieldTimeWaited(i) * 0.001
				self:StartBar(format(q, map), esttime > 1 and esttime or 1, estremain > 1 and estremain or 1, "Interface\\Icons\\INV_Misc_Note_03", "info1", 1500, true)
				currentq[map] = i
			end
		end
		for map, flag in pairs(currentq) do  -- stop inactive bars
			if flag == 0 then
				self:StopBar(format(p, map))
				self:StopBar(format(q, map))
				currentq[map] = nil
			end
		end
	end
end


local temp = { }
local function CheckActive(barid)
	local f = bars[barid]
	if f and f:IsShown() then return f end
end
local function DoReport(this, chan)  -- format chat reports
	if not activebars[this.name] then return end
	local faction = (this.color == "horde" and L["Horde"]) or (this.color == "alliance" and L["Alliance"]) or ""
	SendChatMessage(format(L["%s: %s - %d:%02d"], faction, this.displaytext:GetText(), this.remaining * stamin, this.remaining % 60), chan)
end
local function CheckQueue(name, join)
	local qid = currentq[gsub(name, "^(.+): ", "")]
	if qid and type(qid) == "number" then
		AcceptBattlefieldPort(qid, join) -- leave queue
		return true
	end
end
local function ReportBG(barid)  -- report timer to /bg
	local this = CheckActive(barid)
	if not this then return end
	if not CheckQueue(this.name, true) then
		DoReport(this, "BATTLEGROUND")
	end
end
local function ReportSAY(barid)  -- report timer to /s
	local this = CheckActive(barid)
	if not this then return end
	DoReport(this, "SAY")
end
local function CancelBar(barid)  -- close bar, leave queue if a queue timer
	local this = CheckActive(barid)
	if not this then return end
	if not CheckQueue(this.name, nil) then
		self:StopBar(nil, this)
	end
end
local function BarOnClick(this, button)
	if button == "LeftButton" then
		if IsShiftKeyDown() then
			ReportSAY(this.id)
		elseif IsControlKeyDown() then
			ReportBG(this.id)
		else
			ToggleAnchor()
		end
	elseif button == "RightButton" then
		if IsControlKeyDown() then
			CancelBar(this.id)
		else
			ShowOptions(nil, this.id)
		end
	end
end
local function BarOnUpdate(this, a1)
	this.elapsed = this.elapsed + a1
	if this.elapsed < this.throt then return end
	this.elapsed = 0

	local remain = this.endtime - GetTime()
	this.remaining = remain
	
	if db.fill then
		this.bar:SetValue( ((remain > 0 and this.duration - remain) or this.duration) / this.duration )
	else
		this.bar:SetValue( (remain > 0 and remain or 0.0001) / this.duration )
	end
	if remain < 60 then
		if remain < 10 then  -- fade effects
			if remain > 0.5 then
				this:SetAlpha(0.75 + 0.25 * math_sin(remain * math_pi))
			elseif remain > -1.5 then
				this:SetAlpha((remain + 1.5) * 0.5)
			elseif this.noclose then
				if remain < -this.noclose then
					self:StopBar(nil, this)
				else
					this:SetAlpha(0.7)
					this.throt = 10
				end
				return
			else
				self:StopBar(nil, this)
				return
			end
			this.throt = 0.05
		end
		this.timetext:SetFormattedText("%d", remain < 0 and 0 or remain)
	elseif remain < 600 then
		this.timetext:SetFormattedText("%d:%02d", remain * stamin, remain % 60)
	elseif remain < 3600 then
		this.timetext:SetFormattedText("%dm", remain * stamin)
	else
		this.timetext:SetFormattedText("|cffaaaaaa%d:%02d|r", remain / 3600, remain % 3600 / 60)
	end
end
local function lsort(a, b)
	return bars[a].remaining < bars[b].remaining
end
local function SortBars()
	for i = 1, getn(bars), 1 do
		temp[i] = i
	end
	sort(temp, lsort)
	local pdown, pup = -1, -1
	for _, k in ipairs(temp) do
		local f = bars[k]
		if f:IsShown() then
			if f.down then
				SetPoints(f, "TOPLEFT", bars[pdown] or Capping, "BOTTOMLEFT", 0, -(db.spacing or 1))
				pdown = k
			else
				SetPoints(f, "BOTTOMLEFT", bars[pup] or Capping, "TOPLEFT", 0, db.spacing or 1)
				pup = k
			end
		end
	end
end
local function SetValue(this, frac)
	frac = (frac < 0.0001 and 0.0001) or (frac > 1 and 1) or frac
	this:SetWidth(frac * this.basevalue)
	this:SetTexCoord(0, frac, 0, 1)
end
local function SetReverseValue(this, frac)
	frac = (frac < 0.0001 and 0.0001) or (frac > 1 and 1) or frac
	this:SetWidth(frac * this.basevalue)
	this:SetTexCoord(frac, 0, 0, 1)
end
local function UpdateBarLayout(f)
	local inset, w, h = db.inset, db.width, db.height
	local tc = db.colors.font
	local icon, bar, barback, spark, timetext, displaytext = f.icon, f.bar, f.barback, f.spark, f.timetext, f.displaytext
	SetWH(f, w, h)
	SetWH(icon, h, h)
	SetWH(barback, w - h, h)
	bar:SetHeight(h - 2 * inset)
	spark:SetHeight(2.35 * (h - 2 * inset))
	timetext:SetTextColor(tc.r, tc.g, tc.b, tc.a)
	displaytext:SetTextColor(tc.r, tc.g, tc.b, tc.a)
	if db.iconpos == "HIDE" then  -- icon position
		icon:Hide()
		barback:SetWidth(w)
		SetPoints(barback, "LEFT", f, "LEFT", 0, 0)
	elseif db.iconpos == "RIGHT" then
		icon:Show()
		SetPoints(icon, "RIGHT", f, "RIGHT", 0, 0)
		SetPoints(barback, "RIGHT", icon, "LEFT", 0, 0)
	else
		icon:Show()
		SetPoints(icon, "LEFT", f, "LEFT", 0, 0)
		SetPoints(barback, "LEFT", icon, "RIGHT", 0, 0)
	end
	if db.timepos == "RIGHT" then  -- time text placement
		SetPoints(timetext, "RIGHT", barback, "RIGHT", -(4 + inset), 0)
		SetPoints(displaytext, "LEFT", barback, "LEFT", (4 + inset), 0, "RIGHT", timetext, "LEFT", -(4 + inset), 0)
	else
		SetPoints(displaytext, "LEFT", barback, "LEFT", db.fontsize*3, 0, "RIGHT", barback, "RIGHT", -(4 + inset), 0)
		SetPoints(timetext, "RIGHT", displaytext, "LEFT", -db.fontsize/2.2, 0)
	end
	if db.reverse then  -- horizontal flip of bar growth
		SetPoints(bar, "RIGHT", barback, "RIGHT", -inset, 0)
		spark:SetPoint("CENTER", bar, "LEFT", 0, 0)
		bar.SetValue = SetReverseValue
	else
		SetPoints(bar, "LEFT", barback, "LEFT", inset, 0)
		spark:SetPoint("CENTER", bar, "RIGHT", 0, 0)
		bar.SetValue = SetValue
	end
	bar.basevalue = (w - h) - (2 * inset)
	if f:IsShown() then
		BarOnUpdate(f, 11)
	end
end
-------------------------------------
function Capping:GetBar(name, getone)  -- get active bar or unused/new one
-------------------------------------
	local f, tf
	for k, b in ipairs(bars) do  -- find already assigned bar
		if b.name == name then
			f = b
		elseif getone and not tf and not b:IsShown() then
			tf = b
		end
	end
	if not getone then return f end -- don't assign a new bar

	f = f or tf
	if not f then  -- no available bar, create new one
		local texture = media:Fetch("statusbar", db.texture)
		local font = media:Fetch("font", db.font)
		
		f = CreateFrame("Button", nil, UIParent)
		f:Hide()
		f:RegisterForClicks("LeftButtonUp", "RightButtonUp")
		f:SetScript("OnClick", BarOnClick)
		f:SetScript("OnUpdate", BarOnUpdate)
		f.icon = f:CreateTexture(nil, "ARTWORK")
		f.barback = f:CreateTexture(nil, "BACKGROUND")
		f.barback:SetTexture(texture)
		f.bar = f:CreateTexture(nil, "ARTWORK")
		f.bar:SetTexture(texture)
		
		local spark = f:CreateTexture(nil, "OVERLAY")
		spark:SetTexture("Interface\\CastingBar\\UI-CastingBar-Spark")
		spark:SetBlendMode("ADD")
		spark:SetAlpha(0.8)
		spark:SetWidth(18)
		f.spark = spark
		
		f.timetext = NewText(f, font, db.fontsize, "RIGHT", "CENTER")
		f.displaytext = NewText(f, font, db.fontsize, "LEFT", "CENTER")
		
		UpdateBarLayout(f)
		tinsert(bars, f)
		f.id = getn(bars)
	end
	return f
end
-------------------------------------------------------------------------------------------------------
function Capping:StartBar(name, duration, remaining, icondata, color, nonactive, separate, specialText)
-------------------------------------------------------------------------------------------------------
	if not duration or not remaining then return end
	local icon, l, r, t, b
	if type(icondata) == "table" then
		icon, l, r, t, b = icondata.path, icondata.l, icondata.r, icondata.t, icondata.b
	else
		icon, l, r, t, b = icondata, 0.07, 0.93, 0.07, 0.93
	end
	local c = db.colors[color or "info1"] or db.colors.info1
	if db.onegroup then
		separate = db.mainup
	elseif db.mainup then 
		separate = not separate 
	end
	
	local f = self:GetBar(name, true)
	f.name = name
	f.color = color
	f.endtime = GetTime() + remaining
	f.duration = duration
	f.remaining = remaining
	f.down = not separate
	f.noclose = type(nonactive) == "number" and nonactive
	f.throt = (duration < 300 and 0.1) or (duration < 600 and 0.25) or 0.5
	f.elapsed = f.throt - 0.01
	
	f.displaytext:SetText(specialText or name)
	f.icon:SetTexture(icon)
	f.icon:SetTexCoord(l, r, t, b)
	f.bar:SetVertexColor(c.r, c.g, c.b, c.a or 0.9)
	f.barback:SetVertexColor(c.r * 0.3, c.g * 0.3, c.b * 0.3, db.bgalpha or 0.7)
	f:SetAlpha(1)
	
	activebars[name] = (not nonactive and color) or nil
	f:Show()
	SortBars()
end
------------------------------------
function Capping:StopBar(name, this)
------------------------------------
	local f = this or self:GetBar(name)
	if f then
		f.name = ""
		f:Hide()
		SortBars()
	end
end


local poicons
-------------------------------------------
function Capping:GetIconData(faction, name)
-------------------------------------------
	poicons = poicons or { -- textures used on worldmap { al, ar, at, ab, hl, hr, ht, hb } alliance/horde
		symbol = { 0.875, 0.9375, 0.125, 0.1875, 0, 0.0625, 0.1875, 0.25, },
		flag = { 0.6875, 0.75, 0.125, 0.1875, 0.75, 0.8125, 0.125, 0.1875, },
		graveyard = { 0.25, 0.3125, 0, 0.0625, 0.875, 0.9375, 0, 0.0625, },
		tower = { 0.625, 0.5625, 0, 0.0625, 0.8125, 0.75, 0, 0.0625, },
		farm = { 0, 0.0625, 0.125, 0.1875, 0.125, 0.1875, 0.125, 0.1875, },
		blacksmith = { 0.6875, 0.75, 0.0625, 0.125, 0.8125, 0.875, 0.0625, 0.125, },
		mine = { 0.0625, 0.125, 0.0625, 0.125, 0.1875, 0.25, 0.0625, 0.125, },
		stables = { 0.3125, 0.375, 0.125, 0.1875, 0.4375, 0.5, 0.125, 0.1875, },
		lumbermill = { 0.375, 0.4375, 0.0625, 0.125, 0.5, 0.5625, 0.0625, 0.125, },
		path = "Interface\\Minimap\\POIIcons",
	}
	local t = poicons[name or "symbol"] or poicons.symbol
	if faction == "horde" then
		poicons.l, poicons.r, poicons.t, poicons.b = t[5], t[6], t[7], t[8]
	else
		poicons.l, poicons.r, poicons.t, poicons.b = t[1], t[2], t[3], t[4]
	end
	return poicons
end

--------------------------------
function Capping:GetActiveBars()
--------------------------------
	return activebars
end


local timetil
------------------------------------
function Capping:CheckStartTimer(a1)  -- timer for when a battleground begins
------------------------------------
	timetil = timetil or {
		[strlower(L["1 minute"])] = 63,
		[strlower(L["One minute until"])] = 63,
		[strlower(L["30 seconds"])] = 32,
		[strlower(L["Thirty seconds until"])] = 32,
		[strlower(L["Fifteen seconds until"])] = 16,
	}
	a1 = strlower(a1)
	for text,time in pairs(timetil) do
		if strfind(a1, text) then
			self:StartBar(L["Battleground Begins"], 63, time, "Interface\\Icons\\Spell_Holy_PrayerOfHealing", "info2")
			break
		end
	end
	if IsInArena() and strfind(a1, strlower(L["has begun"])) then  -- Shadow Sight spawn timer
		local spell, _, icon = GetSpellInfo(34709)
		self:StartBar(spell, 93, 93, icon, "info2")
	end
end

local GetBattlefieldScore, GetNumBattlefieldScores = GetBattlefieldScore, GetNumBattlefieldScores
----------------------------------------------
function Capping:GetClassByName(name, faction)  -- retrieves a player's class by name
----------------------------------------------
	for i = 1, GetNumBattlefieldScores(), 1 do
		local iname, _, _, _, _, ifaction, _, _, _, iclass, _, _ = GetBattlefieldScore(i)
		if ifaction == faction and gsub(iname, "-(.+)", "") == name then
			return iclass
		end
	end
end
----------------------------------
function Capping:UpdateCountText()  -- roster counts
----------------------------------
	if not AlwaysUpFrame2 then return end
	local na, nh
	for i = 1, GetNumBattlefieldScores(), 1 do
		local _, _, _, _, _, ifaction = GetBattlefieldScore(i)
		if ifaction == 0 then
			nh = (nh or 0) + 1
		elseif ifaction == 1 then
			na = (na or 0) + 1
		end
	end
	ACountText = ACountText or self:CreateText(AlwaysUpFrame1, 10, "CENTER", AlwaysUpFrame1Icon, 3, 2, AlwaysUpFrame1Icon, -19, 16)
	ACountText:SetText(na or "")
	HCountText = HCountText or self:CreateText(AlwaysUpFrame2, 10, "CENTER", AlwaysUpFrame2Icon, 3, 2, AlwaysUpFrame2Icon, -19, 16)
	HCountText:SetText(nh or "")
end

local classcolor = { }
for class,color in pairs(CUSTOM_CLASS_COLORS or RAID_CLASS_COLORS) do
	classcolor[class] = format("%02x%02x%02x", color.r*255, color.g*255, color.b*255)
end
if CUSTOM_CLASS_COLORS then
	CUSTOM_CLASS_COLORS:RegisterCallback(function()
		for class, color in pairs(CUSTOM_CLASS_COLORS) do
			classcolor[class] = format("%02x%02x%02x", color.r*255, color.g*255, color.b*255)
		end
	end)
end
--------------------------------------
function Capping:GetClassColorHex(cls)  -- retrieves hex color by class (class must be english and in caps)
--------------------------------------
	return classcolor[cls or "PRIEST"] or classcolor["PRIEST"]
end

local function CarrierOnEnter(this)
	if not this.car then return end
	local c = db.colors[strlower(this.faction)] or db.colors.info1
	this:SetBackdropColor(c.r, c.g, c.b, 0.4)
end
local function CarrierOnLeave(this)
	this:SetBackdropColor(0, 0, 0, 0)
end
---------------------------------------------------------------
function Capping:CreateCarrierButton(name, w, h, postclick, oe)  -- create common secure button
---------------------------------------------------------------
	local b = CreateFrame("Button", name, UIParent, "SecureActionButtonTemplate")
	SetWH(b, w, h)
	b:RegisterForClicks("LeftButtonUp")
	b:SetAttribute("type1", "macro")
	b:SetAttribute("macrotext", "")
	b:SetBackdrop(backdrop)
	b:SetBackdropColor(0, 0, 0, 0)
	b:SetScript("PostClick", postclick)
	b:SetScript("OnEnter", CarrierOnEnter)
	b:SetScript("OnLeave", CarrierOnLeave)
	return b
end

---------------------------------------------------------------------------------------
function Capping:CreateText(parent, fontsize, justifyH, tlrp, tlx, tly, brrp, brx, bry)  -- create common text fontstring
---------------------------------------------------------------------------------------
	local text = NewText(parent, GameFontNormal:GetFont(), fontsize, justifyH, "CENTER")
	SetPoints(text, "TOPLEFT", tlrp, "TOPLEFT", tlx, tly, "BOTTOMRIGHT", brrp, "BOTTOMRIGHT", brx, bry)
	text:SetShadowColor(0,0,0)
	text:SetShadowOffset(-1, -1)
	return text
end


do  -- initialize or update a final score estimation bar (AB and EotS uses this)
	local ascore, atime, abases, hscore, htime, hbases = 0, 0, 0, 0, 0, 0
	local updatetime, currentbg
	local f2 = L["Final: 2000 - %d"]
	local ptspersec = {
		[1] = { [0]=0.0, [1]=0.8333, [2]=1.1111, [3]=1.6667, [4]=3.3333, [5]=30.0, }, -- ab
		[2] = { [0]=0.0, [1]=0.5, [2]=1.0, [3]=2.5, [4]=5, }, -- eots
	}
	local sbpattern = {
		[1] = L["Bases: (%d+)  Resources: (%d+)/2000"],  -- ab
		[2] = L["Bases: (%d+)  Victory Points: (%d+)/2000"],  -- eots
	}
	local function getstatedata(off)
		local _, _, text3 = GetWorldStateUIInfo(currentbg + off)
		local base, score = strmatch(text3 or "", sbpattern[currentbg])
		return tonumber(base), tonumber(score)
	end
	local function getlscore(ltime, pps, currentscore)  -- estimate loser's final score
		local lscore = ltime * pps + currentscore
		if currentbg == 1 then
			return format(f2, min(1990, 10 * floor((lscore + 5) * 0.1)))
		else
			return format(f2, min(1999, floor(lscore + 0.5)))
		end
	end
	---------------------------------
	function Capping:NewEstimator(bg)  -- resets estimator and sets new battleground
	---------------------------------
		currentbg = bg
		updatetime = nil
		ascore, atime, abases, hscore, htime, hbases = 0, 0, 0, 0, 0, 0
		self:RegisterTempEvent("UPDATE_WORLD_STATES")
	end
	--------------------------------------
	function Capping:UPDATE_WORLD_STATES()
	--------------------------------------
		local currenttime = GetTime()
		local ABases, AScore  = getstatedata(0)
		local HBases, HScore = getstatedata(1)
		if ABases and HBases then
			abases, hbases = ABases, HBases
			if ascore ~= AScore then
				ascore, atime, updatetime = AScore, currenttime, true
			end
			if hscore ~= HScore then
				hscore, htime, updatetime = HScore, currenttime, true
			end
		end
		
		if not updatetime then return end
		updatetime = nil

		local apps, hpps = ptspersec[currentbg][abases], ptspersec[currentbg][hbases]
		-- timeTil2000 = ((remainingScore) / scorePerSec) - (timeSinceLastUpdate)
		local ATime = ((2000 - ascore) / apps) - (currenttime - atime)
		local HTime = ((2000 - hscore) / hpps) - (currenttime - htime)
		
		local f = self:GetBar("EstFinal")
		local elapsed = (f and f:IsShown() and (f.duration - f.remaining)) or 0
		if HTime < ATime then  -- Horde is winning
			self:StartBar("EstFinal", HTime + elapsed, HTime, self:GetIconData("horde", "symbol"), "horde", nil, nil, getlscore(HTime, apps, ascore))
		else  -- Alliance is winning
			self:StartBar("EstFinal", ATime + elapsed, ATime, self:GetIconData("alliance", "symbol"), "alliance", nil, nil, getlscore(ATime, hpps, hscore))
		end
	end
end


local CappingDD
local info = { }
local abbrv = { av = av, ab = ab, eots = eots, wsg = wsg, winter = winter }
local offsetvalue, offsetcount, lastb, barid
local sbmover, cbmover, seatmover
function ShowOptions(a1, id)
	barid = type(id) == "number" and id
	if not CappingDD then
		CappingDD = CreateFrame("Frame", "CappingDD", Capping)
		CappingDD.displayMode = "MENU"
		local function UpdateLook(k)
			local texture = media:Fetch("statusbar", db.texture)
			local font = media:Fetch("font", db.font)
			local fc = db.colors.font
			self:SetWidth(db.width)
			for index, f in pairs(bars) do
				f.bar:SetTexture(texture)
				f.barback:SetTexture(texture)
				f.timetext:SetFont(font, db.fontsize-1)
				f.displaytext:SetFont(font, db.fontsize)
				f:SetHeight(db.height)
				if k == "colors" or k == "bgalpha" then
					local bc = db.colors[f.color]
					f.bar:SetVertexColor(bc.r, bc.g, bc.b, bc.a or 1)
					f.barback:SetVertexColor(bc.r * 0.3, bc.g * 0.3, bc.b * 0.3, db.bgalpha or 0.7)
					f.timetext:SetTextColor(fc.r, fc.g, fc.b, fc.a)
					f.displaytext:SetTextColor(fc.r, fc.g, fc.b, fc.a)
				end
				if k == "mainup" then f.down = not f.down end
				if k == "onegroup" and db.onegroup then f.down = not db.mainup end
				UpdateBarLayout(f)
			end
			SortBars()
			self:ModMap()
		end
		local function HideCheck(b)
			if b and b.GetName and _G[b:GetName().."Check"] then
				_G[b:GetName().."Check"]:Hide()
			end
		end
		local function CloseMenu(b) 
			if not b or not b:GetParent() then return end
			CloseDropDownMenus(b:GetParent():GetID())
		end
		hooksecurefunc("ToggleDropDownMenu", function(...) lastb = select(8, ...) end)
		local function Exec(b, k, value)
			HideCheck(b)
			if k == "showoptions" then CloseMenu(b) ShowOptions()
			elseif k == "anchor" then ToggleAnchor()
			elseif k == "syncav" and GetRealZoneText() == av then self:SyncAV()
			elseif k == "reportbg" then CloseMenu(b) ReportBG(value)
			elseif k == "reportsay" then CloseMenu(b) ReportSAY(value)
			elseif k == "enterbattle" then CloseMenu(b) ReportBG(value)
			elseif k == "leavequeue" or k == "canceltimer" then CloseMenu(b) CancelBar(value)
			elseif (k == "less" or k == "more") and lastb then
				local off = (k == "less" and -8) or 8
				if offsetvalue == value then
					offsetcount = offsetcount + off
				else
					offsetvalue, offsetcount = value, off
				end
				local tb = _G[gsub(lastb:GetName(), "ExpandArrow", "")]
				CloseMenu(b)
				ToggleDropDownMenu(b:GetParent():GetID(), tb.value, nil, nil, nil, nil, tb.menuList, tb)
			elseif k == "test" then
				local testicon = "Interface\\Icons\\Ability_ThunderBolt"
				self:StartBar(L["Test"].." - "..L["Other"].."1", 90, 90, testicon, "info1", true, true)
				self:StartBar(L["Test"].." - "..L["Other"].."2", 75, 75, testicon, "info2", true, true)
				self:StartBar(L["Test"].." - "..L["Alliance"], 45, 45, testicon, "alliance", true)
				self:StartBar(L["Test"].." - "..L["Horde"], 90, 90, testicon, "horde", true)
				self:StartBar(L["Test"], 75, 75, testicon, "info2", true)
			elseif k == "movesb" then
				sbmover = sbmover or CreateMover(nil, 220, 48, function(this)
					this:StopMovingOrSizing()
					db.sbx, db.sby = floor(this:GetLeft() + 50.5), floor(this:GetTop() - GetScreenHeight() + 10.5)
					WorldStateAlwaysUpFrame:SetPoint("TOP")
				end)
				sbmover:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", WorldStateAlwaysUpFrame:GetLeft() - 50, WorldStateAlwaysUpFrame:GetTop() - 10)
				sbmover:Show()
			elseif k == "movecb" then
				cbmover = cbmover or CreateMover(nil, 173, 27, function(this)
					this:StopMovingOrSizing()
					db.cbx, db.cby = floor(this:GetRight() + 0.5), floor(this:GetTop() + 0.5)
					wsaufu()
				end)
				local x, y = db.cbx or max(0, MinimapCluster:GetRight() - CONTAINER_OFFSET_X), db.cby or max(20, MinimapCluster:GetBottom())
				cbmover:SetPoint("TOPRIGHT", UIParent, "BOTTOMLEFT", x, y)
				cbmover:Show()
			elseif k == "moveseat" then
				seatmover = seatmover or CreateMover(nil, 128, 128, function(this)
					this:StopMovingOrSizing()
					db.seatx, db.seaty = floor(this:GetRight() + 0.5), floor(this:GetTop() + 0.5)
					VehicleSeatIndicator:SetPoint("TOPRIGHT")
				end)
				seatmover:SetPoint("TOPRIGHT", UIParent, "BOTTOMLEFT", VehicleSeatIndicator:GetRight(), VehicleSeatIndicator:GetTop())
				seatmover:Show()
			end
		end
		local function Set(b, k)
			if not k then return end
			db[k] = not db[k]
			if abbrv[k] then  -- enable/disable a battleground while in it
				if GetRealZoneText() == abbrv[k] then
					Capping:ZONE_CHANGED_NEW_AREA()
				elseif k == "winter" and not v then
					self:StopBar(winter)
				end
			else  -- update visual options
				UpdateLook(k)
			end
		end
		local function SetSelect(b, a1)
			db[a1] = tonumber(b.value) or b.value
			local level, num = strmatch(b:GetName(), "DropDownList(%d+)Button(%d+)")
			level, num = tonumber(level) or 0, tonumber(num) or 0
			for i = 1, UIDROPDOWNMENU_MAXBUTTONS, 1 do
				local b = _G["DropDownList"..level.."Button"..i.."Check"]
				if b then
					b[i == num and "Show" or "Hide"](b)
				end
			end
			UpdateLook(a1)
		end
		local function SetColor(a1)
			local dbc = db.colors[UIDROPDOWNMENU_MENU_VALUE]
			if not dbc then return end
			if a1 then
				local pv = ColorPickerFrame.previousValues
				dbc.r, dbc.g, dbc.b, dbc.a = pv.r, pv.g, pv.b, 1 - pv.opacity
			else
				dbc.r, dbc.g, dbc.b = ColorPickerFrame:GetColorRGB()
				dbc.a = 1 - OpacitySliderFrame:GetValue()
			end
			UpdateLook("colors")
		end
		local function AddButton(lvl, text, keepshown)
			info.text = text
			info.keepShownOnClick = keepshown
			UIDropDownMenu_AddButton(info, lvl)
			wipe(info)
		end
		local function AddToggle(lvl, text, value)
			info.arg1 = value
			info.func = Set
			info.checked = db[value]
			AddButton(lvl, text, 1)
		end
		local function AddExecute(lvl, text, arg1, arg2)
			info.arg1 = arg1
			info.arg2 = arg2
			info.func = Exec
			AddButton(lvl, text, 1)
		end
		local function AddColor(lvl, text, value)
			local dbc = db.colors[value]
			if not dbc then return end
			info.hasColorSwatch = true
			info.hasOpacity = 1
			info.r, info.g, info.b, info.opacity = dbc.r, dbc.g, dbc.b, 1 - dbc.a
			info.swatchFunc, info.opacityFunc, info.cancelFunc = SetColor, SetColor, SetColor
			info.value = value
			info.func = UIDropDownMenuButton_OpenColorPicker
			AddButton(lvl, text, nil)
		end
		local function AddList(lvl, text, value)
			info.value = value
			info.hasArrow = true
			info.func = HideCheck
			AddButton(lvl, text, 1)
		end
		local function AddSelect(lvl, text, arg1, value)
			info.arg1 = arg1
			info.func = SetSelect
			info.value = value
			if tonumber(value) then
				if floor(100 * tonumber(value)) == floor(100 * tonumber(db[arg1] or -1)) then
					info.checked = true
				end
			else
				info.checked = (db[arg1] == value)
			end
			AddButton(lvl, text, 1)
		end
		local function AddFakeSlider(lvl, value, minv, maxv, step, tbl)
			local cvalue = 0
			local dbv = db[value]
			if type(dbv) == "string" and tbl then
				for i, v in ipairs(tbl) do
					if dbv == v then
						cvalue = i
						break
					end
				end
			else
				cvalue = dbv or ((maxv - minv) / 2)
			end
			local adj = (offsetvalue == value and offsetcount) or 0
			local starti = max(minv, cvalue - (7 - adj) * step)
			local endi = min(maxv, cvalue + (8 + adj) * step)
			if starti == minv then
				endi = min(maxv, starti + 16 * step)
			elseif endi == maxv then
				starti = max(minv, endi - 16 * step)
			end
			if starti > minv then
				AddExecute(lvl, "--", "less", value)
			end
			if tbl then
				for i = starti, endi, step do
					AddSelect(lvl, tbl[i], value, tbl[i])
				end
			else
				local fstring = (step >= 1 and "%d") or (step >= 0.1 and "%.1f") or "%.2f"
				for i = starti, endi, step do
					AddSelect(lvl, format(fstring, i), value, i)
				end
			end
			if endi < maxv then
				AddExecute(lvl, "++", "more", value)
			end
		end
		CappingDD.initialize = function(self, lvl)
			if lvl == 1 then
				if type(barid) == "number" then
					local bname = bars[barid].name
					info.isTitle = true
					AddButton(lvl, bname)
					if bname == winter then
						AddExecute(lvl, L["Cancel timer"], "canceltimer", barid)
					elseif not activebars[bname] then
						AddExecute(lvl, _G.ENTER_BATTLE, "enterbattle", barid)
						AddExecute(lvl, _G.LEAVE_QUEUE, "leavequeue", barid)
					else
						AddExecute(lvl, L["Send to BG"], "reportbg", barid)
						AddExecute(lvl, L["Send to SAY"], "reportsay", barid)
						AddExecute(lvl, L["Cancel timer"], "canceltimer", barid)
					end
					
					info.isTitle = true
					AddButton(lvl, " ")
					AddExecute(lvl, L["Other options"], "showoptions")
				else
					info.isTitle = true
					AddButton(lvl, "|cff5555ffCapping|r")
					AddList(lvl, _G.BATTLEFIELDS, "battlegrounds")
					AddList(lvl, L["Bar"], "bars")
					AddList(lvl, L["Battlefield Minimap"], "bgmap")
					AddList(lvl, L["Other"], "other")
					AddExecute(lvl, L["Show/Hide Anchor"], "anchor")
				end
			elseif lvl == 2 then
				local sub = UIDROPDOWNMENU_MENU_VALUE
				if sub == "battlegrounds" then
					AddToggle(lvl, av, "av")
					AddToggle(lvl, " -"..L["Auto quest turnins"], "avquest")
					AddExecute(lvl, " -"..L["Request sync"], "syncav")
					AddToggle(lvl, ab, "ab")
					AddToggle(lvl, eots, "eots")
					AddToggle(lvl, wsg, "wsg")
					AddToggle(lvl, winter, "winter")
				elseif sub == "bars" then
					AddList(lvl, L["Texture"], "texture")
					AddList(lvl, L["Width"], "width")
					AddList(lvl, L["Height"], "height")
					AddList(lvl, L["Border width"], "inset")
					AddList(lvl, L["Icons"], "iconpos")
					AddList(lvl, "Spacing", "spacing")
					AddToggle(lvl, L["Fill grow"], "fill")
					AddToggle(lvl, L["Fill right"], "reverse")
					AddToggle(lvl, L["Flip growth"], "mainup")
					AddToggle(lvl, L["Single group"], "onegroup")
					AddList(lvl, L["Font"], "font")
					AddList(lvl, _G.FONT_SIZE, "fontsize")
					AddList(lvl, L["Time position"], "timepos")
					AddList(lvl, _G.COLORS, "color")
					AddList(lvl, _G.BACKGROUND.." ".._G.OPACITY, "bgalpha")
					AddExecute(lvl, L["Test"], "test")
				elseif sub == "bgmap" then
					AddToggle(lvl, L["Narrow map mode"], "narrow")
					AddToggle(lvl, L["Narrow anchor left"], "narrowanchorleft")
					AddToggle(lvl, L["Hide border"], "hidemapborder")
					AddList(lvl, L["Map scale"], "mapscale")
				elseif sub == "other" then
					AddToggle(lvl, L["Port Timer"], "port")
					AddToggle(lvl, L["Wait Timer"], "wait")
					AddExecute(lvl, L["Reposition Scoreboard"], "movesb")
					AddExecute(lvl, L["Reposition Capture Bar"], "movecb")
					AddExecute(lvl, L["Reposition Vehicle Seat"], "moveseat")
				end
			elseif lvl == 3 then
				local sub = UIDROPDOWNMENU_MENU_VALUE
				if sub == "texture" or sub == "font" then
					local t = media:List(sub == "texture" and "statusbar" or sub)
					AddFakeSlider(lvl, sub, 1, #t, 1, t)
				elseif sub == "width" then
					AddFakeSlider(lvl, sub, 20, 600, 2, nil)
				elseif sub == "height" then
					AddFakeSlider(lvl, sub, 2, 100, 1, nil)
				elseif sub == "inset" then
					AddFakeSlider(lvl, sub, 0, 8, 1, nil)
				elseif sub == "iconpos" then
					AddSelect(lvl, L["LEFT"], sub, L["LEFT"])
					AddSelect(lvl, L["RIGHT"], sub, L["RIGHT"])
					AddSelect(lvl, L["HIDE"], sub, L["HIDE"])
				elseif sub == "spacing" then
					AddFakeSlider(lvl, sub, 0, 10, 1, nil)
				elseif sub == "fontsize" then
					AddFakeSlider(lvl, sub, 4, 28, 1, nil)
				elseif sub == "timepos" then
					AddSelect(lvl, L["LEFT"], sub, L["LEFT"])
					AddSelect(lvl, L["RIGHT"], sub, L["RIGHT"])
				elseif sub == "color" then
					AddColor(lvl, L["Alliance"], "alliance")
					AddColor(lvl, L["Horde"], "horde")
					AddColor(lvl, L["Other"].."1", "info1")
					AddColor(lvl, L["Other"].."2", "info2")
					--AddColor(lvl, L["Texture"], "bg")
					AddColor(lvl, L["Font"], "font")
				elseif sub == "bgalpha" then
					AddFakeSlider(lvl, sub, 0, 1, 0.1, nil)
				elseif sub == "mapscale" then
					AddFakeSlider(lvl, sub, 0.2, 5, 0.05, nil)
				end
			end
		end
	end
	ToggleDropDownMenu(1, nil, CappingDD, "cursor")
end
